#include <jimix/IRQDispatcher.h>
#include <jimix/Process.h>
#include <jimix/MonitorDriver.h>
#include <signal.h>
#include <unistd.h>
#include "Tty.h"

extern u32int _process;
extern Tty *ttys[32];
extern LocalTty *activeTty;

extern void set_active_tty(int);

#define CAPSLOCK  0x3a
#define LSHIFT    0x2a
#define RSHIFT    0x36
#define CTRL      0x1d
#define F1        0x3b
#define F2        0x3c
#define F3        0x3d
#define F4        0x3e

char keymap[256];
char keymap_upper[256];
bool capslock = false;
bool shift = false;
bool ctrl = false;

void key_handler(int sig)
{
  while (inb(0x64) & 0x01) // While there is data available.
  {
    u8int scancode = inb(0x60);
   
   if (scancode == 0xe0)
   {
     return;
   }
   
   // Was this a keypress?
   bool keypress = true;
   if (scancode & 0x80)
   {
     keypress = false;
     scancode &= 0x7f;
   }
   
   bool useUpper = false;
   bool useNums = false;
   // Certain scancodes have special meanings.
   switch (scancode)
   {
   case CAPSLOCK:
     if (keypress)
     {
       capslock = !capslock;
     }
     return;
   case LSHIFT:
   case RSHIFT:
     if (keypress)
     {
       shift = true;
     }
     else
     {
       shift = false;
     }
     return;
   case CTRL:
     if (keypress)
     {
       ctrl = true;
     }
     else
     {
       ctrl = false;
     }
     return;
   case F1:
     if (keypress)
     {
       set_active_tty(0);
     }
     return;
   case F2:
     if (keypress)
     {
       set_active_tty(1);
     }
     return;
   case F3:
     if (keypress)
     {
       set_active_tty(2);
     }
     return;
   case F4:
     if (keypress)
     {
       set_active_tty(3);
     }
     return;
   }
   
   if ( (capslock&&!shift) || (!capslock&&shift) )
   {
     useUpper = true;
   }
   if (shift)
   {
     useNums = true;
   }
   
   if (!keypress)
   {
     return;
   }
   
   // TODO: implement a proper lockable buffer.
   char theChar;
   if (scancode < 0x02)
   {
     theChar = keymap[scancode];
   }
   else if (scancode < 0x0e /* backspace */ ||
            (scancode >= 0x1a /*[*/ && scancode <= 0x1b /*]*/) ||
            (scancode >= 0x27 /*;*/ && scancode <= 0x29 /*`*/) ||
            (scancode == 0x2b) ||
            (scancode >= 0x33 /*,*/ && scancode <= 0x35 /*/*/) )
   {
     if (useNums)
     {
       theChar = keymap_upper[scancode];
     }
     else
     {
       theChar = keymap[scancode];
     }
   }
   else if ( (scancode >= 0x10 /*Q*/ && scancode <= 0x19 /*P*/) ||
             (scancode >= 0x1e /*A*/ && scancode <= 0x26 /*L*/) ||
             (scancode >= 0x2c /*Z*/ && scancode <= 0x32 /*M*/) )
   {
     if (useUpper)
     {
       theChar = keymap_upper[scancode];
     }
     else
     {
       theChar = keymap[scancode];
     }
   }
   else if (scancode <= 0x39 /* space */)
   {
     theChar = keymap[scancode];
   }
   else
   {
     return;
   }
   activeTty->inputReceived(theChar);
   termios_t t;
   activeTty->tcgetattr(&t);
   if (t.c_lflag & ECHO)
   {
     if ( (t.c_lflag & ECHONL) && 
          (theChar == '\n') )
     {
       activeTty->write(theChar);
     }
     else if ( (t.c_lflag & ECHOE) &&
               (theChar == (char)0x08) )
     {
       activeTty->backspace();
     }
     else if (theChar != '\n' && theChar != (char)0x08)
     {
       activeTty->write(theChar);
     }
   }
  }
}

void keyboard_init()
{
  activeTty->write((char*)"Keyboard init!\n");
  signal(18, &key_handler);
  u32int irqDispatcher = requestResource((char*)"IRQDispatcher");
  callResource(irqDispatcher, IRQDispatcher_IF::registerHandler, 1, getpid(), 18);
  
  while (inb(0x64) & 0x01) // While there is data available.
  {
    activeTty->write((char*)"Keyboard has data!\n");
    u8int scancode = inb(0x60);
  }
  activeTty->write((char*)"Keyboard finishez dataz!\n");
  keymap[0x00] = 0x00;
  keymap[0x01] = 0x1b; // Escape
  keymap[0x02] = '1';
  keymap[0x03] = '2';
  keymap[0x04] = '3';
  keymap[0x05] = '4';
  keymap[0x06] = '5';
  keymap[0x07] = '6';
  keymap[0x08] = '7';
  keymap[0x09] = '8';
  keymap[0x0a] = '9';
  keymap[0x0b] = '0';
  keymap[0x0c] = '-';
  keymap[0x0d] = '=';
  keymap[0x0e] = 0x08; // Backspace
  keymap[0x0f] = 0x09; // Tab
  keymap[0x10] = 'q';
  keymap[0x11] = 'w';
  keymap[0x12] = 'e';
  keymap[0x13] = 'r';
  keymap[0x14] = 't';
  keymap[0x15] = 'y';
  keymap[0x16] = 'u';
  keymap[0x17] = 'i';
  keymap[0x18] = 'o';
  keymap[0x19] = 'p';
  keymap[0x1a] = '[';
  keymap[0x1b] = ']';
  keymap[0x1c] = '\n'; // Enter
  keymap[0x1d] = 0x00; // LCtrl
  keymap[0x1e] = 'a';
  keymap[0x1f] = 's';
  keymap[0x20] = 'd';
  keymap[0x21] = 'f';
  keymap[0x22] = 'g';
  keymap[0x23] = 'h';
  keymap[0x24] = 'j';
  keymap[0x25] = 'k';
  keymap[0x26] = 'l';
  keymap[0x27] = ';';
  keymap[0x28] = '\'';
  keymap[0x29] = '`';
  keymap[0x2a] = 0x00; // LShift
  keymap[0x2b] = '\\';
  keymap[0x2c] = 'z';
  keymap[0x2d] = 'x';
  keymap[0x2e] = 'c';
  keymap[0x2f] = 'v';
  keymap[0x30] = 'b';
  keymap[0x31] = 'n';
  keymap[0x32] = 'm';
  keymap[0x33] = ',';
  keymap[0x34] = '.';
  keymap[0x35] = '/';
  keymap[0x36] = 0x00; // RShift
  keymap[0x37] = '*';  // Keypad-*
  keymap[0x38] = 0x00; // LAlt
  keymap[0x39] = ' ';
  
  keymap_upper[0x02] = '!';
  keymap_upper[0x03] = '@';
  keymap_upper[0x04] = 0x00;
  keymap_upper[0x05] = '$';
  keymap_upper[0x06] = '%';
  keymap_upper[0x07] = '^';
  keymap_upper[0x08] = '&';
  keymap_upper[0x09] = '*';
  keymap_upper[0x0a] = '(';
  keymap_upper[0x0b] = ')';
  keymap_upper[0x0c] = '_';
  keymap_upper[0x0d] = '+';
  keymap_upper[0x10] = 'Q';
  keymap_upper[0x11] = 'W';
  keymap_upper[0x12] = 'E';
  keymap_upper[0x13] = 'R';
  keymap_upper[0x14] = 'T';
  keymap_upper[0x15] = 'Y';
  keymap_upper[0x16] = 'U';
  keymap_upper[0x17] = 'I';
  keymap_upper[0x18] = 'O';
  keymap_upper[0x19] = 'P';
  keymap_upper[0x1a] = '{';
  keymap_upper[0x1b] = '}';
  keymap_upper[0x1e] = 'A';
  keymap_upper[0x1f] = 'S';
  keymap_upper[0x20] = 'D';
  keymap_upper[0x21] = 'F';
  keymap_upper[0x22] = 'G';
  keymap_upper[0x23] = 'H';
  keymap_upper[0x24] = 'J';
  keymap_upper[0x25] = 'K';
  keymap_upper[0x26] = 'L';
  keymap_upper[0x27] = ':';
  keymap_upper[0x28] = '"';
  keymap_upper[0x29] = '~';
  keymap_upper[0x2b] = '|';
  keymap_upper[0x2c] = 'Z';
  keymap_upper[0x2d] = 'X';
  keymap_upper[0x2e] = 'C';
  keymap_upper[0x2f] = 'V';
  keymap_upper[0x30] = 'B';
  keymap_upper[0x31] = 'N';
  keymap_upper[0x32] = 'M';
  keymap_upper[0x33] = '<';
  keymap_upper[0x34] = '>';
  keymap_upper[0x35] = '?';
  
  
}
